package net.gdface.sdk.fse;

import gu.sql2java.BaseRow;
import gu.sql2java.Managers;
import gu.sql2java.TableListener;
import gu.sql2java.TableManager.Action;
import gu.sql2java.exception.RuntimeDaoException;
import net.gdface.sdk.fse.CodeBean;
import net.gdface.sdk.fse.FeatureSe;
import net.gdface.sdk.fse.FeatureSeDecorator;
import static net.gdface.utils.SimpleLog.log;

import java.lang.reflect.ParameterizedType;
import java.util.concurrent.atomic.AtomicInteger;

import static com.google.common.base.Preconditions.*;
/**
 * 基于数据库的特征搜索引擎
 * 依赖库: 
 * <ul>
 * <li>groupId: com.gitee.l0km</li>
 * <li>artifactId: sql2java-manager</li>
 * </ul>
 * @author guyadong
 *
 * @param <F> 保存特征数据的记录
 */
public abstract class BaseFseDbEngine<F extends BaseRow>{
	protected final FeatureSeDecorator fse;
	private final Class<F> rawType;
	/**
	 * 
	 * @param fse {@link FeatureSe}引擎实例
	 */
	@SuppressWarnings("unchecked")
	public BaseFseDbEngine(FeatureSe fse) {
		this.fse= FeatureSeDecorator.makeDecorator(checkNotNull(fse,"fse is null"));
		rawType = (Class<F>) ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * 加载数据库中所有特征到FSE内存表
	 */
	protected void loaddb(){
		log("Loading feature data from Database into memory...");
		final AtomicInteger rowCount = new AtomicInteger(0);
		Managers.managerOf(rawType).loadAll(new Action<F>(){
			@Override
			public void call(F bean) throws RuntimeDaoException {
				addFeatureBean(bean);	
				showProgress(rowCount.incrementAndGet());
			}
		});
	}
	/**
	 * 搜索引擎初始化
	 * @param dao Dao对象
	 */
	public void init(){
		loaddb();
		Managers.managerOf(rawType).registerListener(featureTableListener);
	}
	/** 显示完成进度 */
	private void showProgress(int c){
		// 显示完成进度
		if(0==c%100000){
			System.out.printf("*");
			if(0==(c%1000000)){							
				if(0==(c%=10000000)){
					System.out.println();
				}else{
					System.out.print(" ");
				}
			}
		}
	}
	
	private final TableListener<F> featureTableListener = 
		new TableListener.Adapter<F>(){	
			@Override
			public void afterInsert(F bean) throws RuntimeDaoException {								
				addFeatureBean(bean);
			}
			@Override
			public void afterDelete(F bean) throws RuntimeDaoException {
				fse.removeFeature(featureIdOf(bean));
			}
		};
	
	protected void addFeatureBean(F bean){
		if(beanFilter(bean)){
			byte[] feature = featureOf(bean);
			if(null != feature){
				fse.addFeature(
						featureIdOf(bean),
						feature, ownerOf(bean));
			}
		}
	}
	
	/**
	 * 从特征记录中返回特征数据的MD5
	 * @param bean
	 * @return MD5校验码(16字节数组)
	 */
	protected byte[] featureIdOf(F bean){
		return null;
	}
	/**
	 * 从特征记录中返回特征所属者ID
	 * @param bean
	 * @return 32字节HEX字符串
	 */
	protected String ownerOf(F bean){
		return null;
	}

	/**
	 * 从特征记录中返回特征数组
	 * @param bean
	 * @return 特征数据(二进制数组)
	 */
	protected abstract byte[] featureOf(F bean);
	
	/**
	 * 数据库记录过滤器函数
	 * @param bean
	 * @return 返回{@code true}则加入FSE,否则跳过
	 */
	protected boolean beanFilter(F bean) {
		return bean != null;
	}
	/**
	 * @param feature  要比对的特征码
	 * @param similarty 相似度阀值
	 * @param rows 最多返回的记录数目
	 * @return 返回包含相似度(降序)的结果数组,如果没有查到匹配的记录则返回空数组
	 * @see FeatureSe#searchCode(byte[], double, int)
	 */
	public CodeBean[] searchFeatures(byte[] feature, double similarty, int rows) {
		return fse.searchCode(checkNotNull(feature,"feature is null"),similarty,rows);
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("BaseFseDbEngine [fseClassName=");
		builder.append(fse);
		builder.append("]");
		return builder.toString();
	}
}
